package com.ns002.yigou.quote.common.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ns002.yigou.quote.common.app.QuoteDate;
import com.ns002.yigou.quote.common.connection.EBuyConnection;
import com.ns002.yigou.quote.domain.OpenCloseDate;
import com.ns002.yigou.quote.domain.quotekline.result.LastNodeResult;
import com.ns002.yigou.quote.entity.KLine;
import com.ns002.yigou.quote.entity.RealTimeQuote;
import com.ns002.yigou.quote.globals.RedisKey;
import com.ns002.yigou.quote.json.QueryQuote;
import com.ns002.yigou.quote.service.RealTimeQuoteService;
import com.opslab.util.DateUtil;
import com.opslab.util.StringUtil;
import com.opslab.util.redis.CacheUtils;
import com.opslab.util.redis.ExpireTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by xu on 2017/1/12.
 * 实时行情拉取
 */
@Component
public class LoadRealTimeQuoteTask {

    Logger log = LoggerFactory.getLogger(LoadRealTimeQuoteTask.class);
    @Autowired
    EBuyConnection eBuyConnection;
    @Autowired
    RealTimeQuoteService realTimeQuoteService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${quote.save}")
    private boolean quoteSvae;

    //ebuy行情服务器登录令牌
    static String token;

    Map<String, QueryQuote> lastMap = new HashMap<>();

    public void loadEBuy() {
        try {
            if (StringUtil.isEmpty(token)) {
                return;
            }
            Map<String, QueryQuote> quoteMap = eBuyConnection.realTime(token);
            run(quoteMap);
        } catch (Throwable e) {
            e.printStackTrace();
            log.error("吉交所所行情拉取失败");
        }
    }


    public void loadEBuyQuoteToken() {
        try {
            token = eBuyConnection.gainPriceServerToken();
            log.error("生成token为：" + token);
        } catch (Throwable e) {
            e.printStackTrace();
            log.error("汇率行情token拉取失败");
        }
    }


    public void run(Map<String, QueryQuote> quoteMap) {
        try {

            if (quoteMap == null || quoteMap.size() == 0) {
                return;
            }

            for (String key : quoteMap.keySet()) {
                QueryQuote data = quoteMap.get(key);

                if (data == null) {
                    continue;
                }
                String contract = key;
                data.setContract(key);

                //查询
                if (lastMap.get(contract) == null) {

                    //判断数据库数据
                    RealTimeQuote realTimeQuoteEntity = realTimeQuoteService.findLast(contract);

                    if (realTimeQuoteEntity != null) {
                        QueryQuote queryQuote = new QueryQuote();
                        queryQuote.setQuote(realTimeQuoteEntity.getQuote().toString());
                        queryQuote.setNewDate(new Date(realTimeQuoteEntity.getTimestamp()));
                        lastMap.put(contract, queryQuote);
                    }
                }


                if (lastMap.get(contract) != null) {

                    //行情时间点出现更新
                    if (lastMap.get(contract).getNewDate().getTime() != data.getNewDate().getTime() ||
                            !lastMap.get(contract).getQuote().equals(data.getQuote())) {
                        //时间点改变， 或者同时间点，点位改变时
                        saveQuote(data);

                        //匹配报价有无更新
                        if (lastMap.get(contract).getQuote().equals(data.getQuote())) {

                            //记录旧的报价
                            lastMap.put(contract, data);

                            //没有最新报价
                            continue;
                        }
                    } else {
                        continue;
                    }

                }
//                //设置产品名
//                Product product = productCache.getProductByContract(key);
//                data.setProductName(product.getContractName());
//                data.setOrg(product.getOrg());
//                data.setContract(product.getContract());

                //设置开盘休市时间  找到有数据为止
                Date quoteDate = new Date();
                while (true) {
                    OpenCloseDate openCloseDate = QuoteDate.getOpenCloseDateByAlias(contract, quoteDate);
                    if (openCloseDate == null || openCloseDate.getOpenDate() == null) {
                        quoteDate = new Date(quoteDate.getTime() - 60000 * 60 * 24);
                        continue;
                    }
                    data.setOpenDate(openCloseDate.getOpenDate());
                    data.setCloseDate(openCloseDate.getCloseDate());
                    break;
                }

                updateCache(data);

                //记录旧的报价
                lastMap.put(contract, data);

            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存行情数据
     *
     * @param queryQuote 行情
     */
    private void saveQuote(QueryQuote queryQuote) {

        try {

            if (quoteSvae) {
                RealTimeQuote realTimeQuoteEntity = new RealTimeQuote();
                realTimeQuoteEntity.setQuote(Double.parseDouble(queryQuote.getQuote()));
                realTimeQuoteEntity.setContract(queryQuote.getContract());
                realTimeQuoteEntity.setTimestamp(queryQuote.getNewDate().getTime() + new Date().getTime() % 1000);

                realTimeQuoteService.save(realTimeQuoteEntity);

                //更新最后K线点位
                updateLastKLine(queryQuote.getContract(), Double.parseDouble(queryQuote.getQuote()), queryQuote.getNewDate().getTime());
            }

        } catch (Throwable e) {
            log.error("更新行情出现问题", e);
        }
    }

    /**
     * 更新行情缓存
     *
     * @param queryQuote
     */
    private void updateCache(QueryQuote queryQuote) {
        //更新缓存
        Map<String, QueryQuote> quoteMap = JSON.parseObject(stringRedisTemplate.opsForValue().get(RedisKey.QUOTE_CACHE), new TypeReference<Map<String, QueryQuote>>() {
        });

        if (quoteMap == null) quoteMap = new HashMap<>();

        quoteMap.put(queryQuote.getContract(), queryQuote);
        stringRedisTemplate.opsForValue().set(RedisKey.QUOTE_CACHE, JSON.toJSONString(quoteMap));

    }

    /**
     * 更新最后的K线节点
     *
     * @param quote
     * @param quoteTime
     */
    private void updateLastKLine(String contract, double quote, long quoteTime) {

        //五分钟
        handleLine("M5", contract, quote, quoteTime, ExpireTime.FIVE_MIN);

        //十五分钟
        handleLine("M15", contract, quote, quoteTime, ExpireTime.FIFTEEN_MIN);

        //三十分钟
        handleLine("M30", contract, quote, quoteTime, ExpireTime.HALF_AN_HOUR);

        //一小时
        handleLine("H1", contract, quote, quoteTime, ExpireTime.HALF_AN_HOUR);

        //两小时
        handleLine("H2", contract, quote, quoteTime, ExpireTime.TOW_HOUR);

        //四小时
        handleLine("H4", contract, quote, quoteTime, ExpireTime.FOUR_HOUR);

        //一天
        handleLine("D1", contract, quote, quoteTime, ExpireTime.ONE_DAY);

    }

    /**
     * 处理K线节点
     *
     * @param type       类型
     * @param quote      最新行情
     * @param quoteTime  最新行情时间
     * @param expireTime 缓存有效时间
     */
    private void handleLine(String type, String contract, double quote, long quoteTime, ExpireTime expireTime) {
        String key = getLastKLineKey(type, contract, quoteTime);
        LastNodeResult result = JSON.parseObject(CacheUtils.getStr(key), LastNodeResult.class);

        result = loadKlineNode(result, type, quote, quoteTime);

        CacheUtils.set(key, JSON.toJSONString(result), expireTime);
    }

    /**
     * 获取key
     *
     * @param type
     * @param quoteTime
     * @return
     */
    private static String getLastKLineKey(String type, String contract, long quoteTime) {
        return RedisKey.QUOTE_LAST_KLINE + contract + type + klineNodeTime(type, quoteTime);
    }

    /**
     * 获取最后的K线点位
     *
     * @param type
     * @return
     */
    public static LastNodeResult getLastKLine(String type, String contract) {
        long currentTime = new Date().getTime();
        String key = getLastKLineKey(type, contract, currentTime);

        String value = CacheUtils.getStr(key);

        return StringUtil.isNotEmpty(value) ? JSON.parseObject(CacheUtils.getStr(key), LastNodeResult.class) : null;
    }


    private LastNodeResult loadKlineNode(LastNodeResult kline, String type, double quote, long quoteTime) {

        if (kline == null) {
            kline = new LastNodeResult();

            long nodeTime = klineNodeTime(type, quoteTime);
            kline.setStartDate(new Date(nodeTime));
            kline.setEndDate(new Date(nodeTime + klineNodeSpace(type)));

        }

        if (StringUtil.isEmpty(kline.getHigh())) {  //高
            kline.setHigh(String.valueOf(quote));
        } else {
            if (quote > Double.parseDouble(kline.getHigh())) {
                kline.setHigh(String.valueOf(quote));
            }
        }

        if (StringUtil.isEmpty(kline.getOpen())) {  //开
            kline.setOpen(String.valueOf(quote));
        }

        if (StringUtil.isEmpty(kline.getLow())) {  //低
            kline.setLow(String.valueOf(quote));
        } else {
            if (quote < Double.parseDouble(kline.getLow())) {
                kline.setLow(String.valueOf(quote));
            }
        }

        kline.setClose(String.valueOf(quote));      //收

        return kline;
    }

    private static long klineNodeTime(String type, long quoteTime) {
        return quoteTime - (quoteTime % klineNodeSpace(type));
    }

    private static long klineNodeSpace(String type) {
        long space = 0;
        switch (type) {
            case "M5":
                space = 60000 * 5;
                break;
            case "M15":
                space = 60000 * 15;
                break;
            case "M30":
                space = 60000 * 30;
                break;
            case "H1":
                space = 60000 * 60;
                break;
            case "H2":
                space = 60000 * 60 * 2;
                break;
            case "H4":
                space = 60000 * 60 * 4;
                break;
            case "D1":
                space = 60000 * 60 * 24;
                break;
        }

        return space;
    }


}
